export {} //防止重名
import cheerio from 'cheerio' //引入选择器
import superagent from 'superagent' //这个库类似axios,用来发起请求
import fs from 'fs' //引入node内置模块
import path from 'path' //引入node内置模块

//定义接口包含title和desc并且规定类型为string
interface Target {
  title: string
  desc: string
}

//定义接口包含time/status/Target接口
interface retData {
  time: number
  status: number
  data: Target[]
}

//定义文件数据接口
interface FileDate {
  [propName: string]: Target[]
}

class Crawler {
  //定义访问地址 属性为私有属性
  private url = `https://toscrape.com/`

  //拿到数据方法
  async getComposition() {
    //异步请求数据
    const responseRet = await superagent.get(this.url) //获取html

    //拿到想要数据内容
    const responseHtml = responseRet.text

    //返回拿到的数据
    return responseHtml
  }
  getTargetDate(html: string) {
    //定义空数组,并且对数据类型进行约束
    const targetDate: Target[] = []

    //相当于的jQuery选择器 用cheerio.load包裹一下数据变成jQuery选择器
    const $ = cheerio.load(html)

    //拿到第二个table,然后查找tr元素,还需要用$包裹一下
    const trList = $($('.table')[1]).find('tr')

    //对选中的数据进行遍历 并且返回一个新数组
    trList.map((index, item) => {
      //逻辑判断
      if (index > 0) {
        //拿到a的内容
        const title = $(item).find('a').text()

        //拿到td的内容
        const desc = $(item).find('td').text()

        //把数据填充进数组
        targetDate.push({ title: title, desc: desc })
      }
    })

    //自定义数据格式
    const retData: retData = {
      time: new Date().getTime(),
      status: 200,
      data: targetDate,
    }
    return retData
  }

  assemblyData(data: retData) {
    //其实这个函数对数据进行组合

    //定义空数据
    const fileDate: FileDate = {}

    //拿到文件路径
    const filePath = path.resolve('../data/ret.json')

    //判断是否存在文件 也就是判断之前是否已经请求过
    if (fs.existsSync(filePath)) {
      //说明文件存在
      //重新定义文件,定义文件路径和文件读取格式
      fileDate[data.time] = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
    }

    //如果不存在文件,重新定义数据
    fileDate[data.time] = data.data

    //返回文件数据
    return fileDate
  }
  //此方法调用 把所有的逻辑代码进行组合
  async crewProcess() {
    //拿到文件路径
    const filePath = path.resolve('data/ret.json')

    //1、爬取HTML结构
    const retHtml = await this.getComposition()

    //2、获取数据
    const resData = await this.getTargetDate(retHtml)

    //把数据变成想要的格式
    const fileData = this.assemblyData(resData)

    //写入文件
    fs.writeFileSync(filePath, JSON.stringify(fileData))
  }
  constructor() {
    //调用方法
    this.crewProcess()
  }
}

const crawler = new Crawler()
